import derelict.sdl.sdl;
import Pixels;
import Point;
import Color;
import Lines;
import Circles;

const float SKOK = 0.001;

public class Curve{
	point[] pkty;
	uint curveColor;
	uint lineColor;
	uint pointColor;
	
	this(point[] pkty, uint curveColor, uint lineColor = 0xFFFFFF, uint pointColor = 0xFF0000){
		this.pkty.length = pkty.length;
		this.pkty[] = pkty[];
		this.curveColor = curveColor;
		this.lineColor = lineColor;
		this.pointColor = pointColor;
	}

	void draw(SDL_Surface *surface, bool drawLine = false, bool drawPoints = false){
		assert(false);
	}

	point* getNearestPoint(point p, uint maxDist){
		foreach(inout pkt; pkty)
			if(pkt.dist2(p) <= maxDist*maxDist)
				return &pkt;
		return null;
	}

}

public class BezierCurve:Curve{

	this(point[] pkty, uint curveColor, uint lineColor = 0xFFFFFF, uint pointColor = 0xFF0000){
		writefln(pkty.length);
		assert(pkty.length == 4);
		super(pkty, curveColor, lineColor, pointColor);
	}

	float be(float t,point[] pkty, int wsp){
		float res;
	
		res =	pkty[0].coord[wsp]	*(1-t)*(1-t)*(1-t) +
				3*pkty[1].coord[wsp]*t*(1-t)*(1-t) +
				3*pkty[2].coord[wsp]*t*t*(1-t) +
				pkty[3].coord[wsp]	*t*t*t;
		return res;
	}

	override void draw(SDL_Surface *surface, bool drawLine = false, bool drawPoints = false){
		float t = 0;
		if(drawLine){
			line(surface, pkty[0], pkty[1], lineColor);
			line(surface, pkty[2], pkty[3], lineColor);
		}
		if(drawPoints)
			foreach(pkt; pkty){
				putPixel(surface, pkt.x, pkt.y, pointColor);
				circle(surface, pkt.x, pkt.y, 2, pointColor);
			}
		while(t<=1){
			putPixel(surface,cast(int)(be(t,pkty,0)), cast(int)(be(t,pkty,1)), curveColor);
			t += SKOK;
		}
	}

}

public class BSplineCurve:Curve{

	this(point[] pkty, uint curveColor, uint lineColor = 0xFFFFFF, uint pointColor = 0xFF0000){
		super(pkty, curveColor, lineColor, pointColor);
	}

	float be(float t,point[] pkty, int trans,  int wsp){
		float res;
	
		res =	pkty[0 + trans].coord[wsp]	*(1-t)*(1-t)*(1-t) +
				pkty[1 + trans].coord[wsp]*(4-6*t*t+3*t*t*t) +
				pkty[2 + trans].coord[wsp]*(1+3*t+3*t*t-3*t*t*t) +
				pkty[3 + trans].coord[wsp]	*t*t*t;
		res /= 6.f;
		return res;
	}

	override void draw(SDL_Surface *surface, bool drawLine = false, bool drawPoints = false){
		float t;
		if(drawLine){
			for(int i = 1; i < pkty.length; i++)
				line(surface, pkty[i-1], pkty[i], lineColor);
		}
		if(drawPoints)
			foreach(pkt; pkty){
				putPixel(surface, pkt.x, pkt.y, pointColor);
				circle(surface, pkt.x, pkt.y, 2, pointColor);
			}
		for(int i = 0; i < pkty.length - 3; i++){
		t = 0;
			while(t<=1){
				putPixel(surface,cast(int)(be(t,pkty,i,0)), cast(int)(be(t,pkty,i,1)), curveColor);
				t += SKOK;
			}
		}
	}

}

public class NURBSCurve:Curve{
	const int degree = 3;
	float[] knots;

	this(point[] pkty, uint curveColor, uint lineColor = 0xFFFFFF, uint pointColor = 0xFF0000){
		super(pkty, curveColor, lineColor, pointColor);
//		assert(pkty.length == 7);
		genKnots(pkty.length, degree+1);
//		knots.length = degree + pkty.length + 1;
//		knots[] = [0.0f,0.0f,0.0f,0.0f,1.0f,2.0f,3.0f,4.0f,4.0f,4.0f,4.0f];
	}

	void genKnots(int n, int c){
		knots.length = n + c;
		knots[0] = knots[1] = 0;
		for(int i = 2; i < n + c+1; i++)
			if((i > c) && (i < n+2))
				knots[i-1] = knots[i-2] + 1;
			else
				knots[i-1] = knots[i-2];
		writefln(knots);
	}

	float deBoor(float u, int i, int k){
		float ret = 0;

		if(k == 1){
			if(knots[i] <= u && u<= knots[i+1])
				return 1.f;
			return .0f;
		}
		float den1 = knots[i + k - 1] - knots[i];
		float den2 = knots[i + k] - knots[i + 1];
		
		if(den1 > 0)
			ret += ((u-knots[i])/den1) * deBoor(u,i,k-1);
		if(den2>0)
			ret += (knots[i+k]-u) / den2 * deBoor(u, i+1, k-1);

		return ret;
	}

	override void draw(SDL_Surface *surface, bool drawLine = false, bool drawPoints = false){
		float t = 0;
		float x,y;
		if(drawLine){
			for(int i = 1; i < pkty.length; i++)
				line(surface, pkty[i-1], pkty[i], lineColor);
		}
		if(drawPoints)
			foreach(pkt; pkty){
				putPixel(surface, pkt.x, pkt.y, pointColor);
				circle(surface, pkt.x, pkt.y, 2, pointColor);
			}
		while(t<=knots[$-1]){
			x = y = 0;
			for(int i = 0; i < pkty.length; i++){
				float val = deBoor(t, i, degree+1);
				if(val > 0.0001f){
					x += val * pkty[i].x;
					y += val * pkty[i].y;
				}
			}
			putPixel(surface,cast(int)(x), cast(int)(y), curveColor);
			t += SKOK;
		}

	}



}
